// make_shared vs 直接构造 shared_ptr 的内存分配比较
// 构造函数调用次数
// 首先，澄清一个概念：无论使用哪种方式，对象的构造函数都只会被调用一次。
// 区别在于内存分配的次数。

// 使用 make_shared
auto ptr = std::make_shared<MyClass>(arg1, arg2);
// 对象构造函数调用：1次（MyClass的构造函数）
// 内存分配：1次（同时为对象和控制块分配内存）

// 直接构造 shared_ptr
std::shared_ptr<MyClass> ptr(new MyClass(arg1, arg2));
// 对象构造函数调用：1次（MyClass的构造函数）
// 内存分配：2次（一次为对象分配内存，一次为控制块分配内存）

// 内存布局对比
// make_shared 的内存布局
单次分配的内存块
+---------------------------+
| 控制块 | MyClass对象数据  |
| (引用计数等)              |
+---------------------------+


// 直接构造 shared_ptr 的内存布局

两次分配的内存块
+---------------+    +---------------+
| 控制块        |    | MyClass对象   |
| (引用计数等)  |    | 数据          |
+---------------+    +---------------+


// 详细解析
// make_shared 的工作原理
// 分配一块足够大的内存，能同时容纳控制块和对象
// 在这块内存的前部构造控制块
// 在这块内存的后部构造对象（调用一次对象构造函数）
// 返回指向这块内存的 shared_ptr

// 直接构造 shared_ptr 的工作原理
// new MyClass(arg1, arg2) 分配内存并构造对象（调用一次对象构造函数）
// shared_ptr 构造函数接收这个指针
// shared_ptr 构造函数分配另一块内存用于控制块
// 设置控制块指向对象

// 性能优势
// make_shared 的性能优势：

// 减少内存分配次数：一次分配而不是两次，减少内存分配的开销
// 提高缓存局部性：控制块和对象在内存中相邻，可能提高缓存命中率
// 减少内存碎片：单次分配减少内存碎片的可能性

// 何时不能使用make函数
// 虽然make_shared和make_unique很好，但有些情况下不能使用它们：
// 1. 需要自定义删除器时：
// 不能使用make_shared
std::shared_ptr<File> filePtr(new File("data.txt"), 
                             [](File* f){ fclose(f->handle); delete f; });

// 2. 需要使用大括号初始化时：
// 不能直接使用make_shared
auto initList = {1, 2, 3, 4};
std::shared_ptr<std::vector<int>> vecPtr(new std::vector<int>{initList});

// 但可以这样做
auto vecPtr = std::make_shared<std::vector<int>>(initList.begin(), initList.end());